package com.example.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import static com.example.utils.Const.JWT_BLACK_LIST;

//JWT工具类
@Component
public class JwtUtil {

    @Value("${spring.security.jwt.key}")
    String key;

    @Value("${spring.security.jwt.expire}")
    int expire;

    /*注意：@Resource是默认取字段名进行按照名称注入*/
    @Resource
    StringRedisTemplate stringRedisTemplate;

    //根据登录用户的信息（UserDetails）创建jwt令牌
    public String createJwt(UserDetails userDetails, int id, String name) {
        Algorithm algorithm = Algorithm.HMAC256(key);
        Date expire = this.expireTime();
        return JWT
                .create()
                //添加一个随机的UUID，用于唯一标识token，以便后面删除
                .withJWTId(UUID.randomUUID().toString())
                .withClaim("id", id)
                .withClaim("username", name)
                .withClaim("authorities", userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())
                .withExpiresAt(expire)
                .withIssuedAt(new Date())
                .sign(algorithm);
    }

    //转换token
    private String convertToken(String tokenInHeader) {
        //检查合法性
        if (tokenInHeader == null || !tokenInHeader.startsWith("Bearer "))
            return null;
        return tokenInHeader.substring(7);
    }

    //从请求头中解析出jwt
    public DecodedJWT resolveJwt(String tokenInHeader) {
        //从请求头中搞出token
        String token = this.convertToken(tokenInHeader);
        if (token == null) return null;
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            //检查当前的token是否有效（是否在黑名单）
            if(this.idInvalidate(decodedJWT.getId())){
                return null;
            }
            //保证token不在黑名单，再检查令牌是否过期
            Date expire = decodedJWT.getExpiresAt();
            //TODO:after看不懂思密达
            return new Date().after(expire) ? null : decodedJWT;
        } catch (Exception e) {
            return null;
        }
    }


    //解析成UserDetail
    public UserDetails toUser(DecodedJWT jwt) {
        Map<String, Claim> claims = jwt.getClaims();
        return User
                .withUsername(claims.get("username").toString())
                .password("*******")//没存所以随便写写
                .authorities(claims.get("authorities").asArray(String.class))
                .build();
    }

    //解析出Id
    public Integer toId(DecodedJWT jwt) {
        Map<String, Claim> claims = jwt.getClaims();
        Integer id = claims.get("id").asInt();
        return id;
    }


    //计算过期时间
    public Date expireTime() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.HOUR, expire * 24);
        return calendar.getTime();
    }

    //失效Jwt
    public boolean invalidateJwt(String tokenInHeader) {
        //从请求头中搞出token
        String token = this.convertToken(tokenInHeader);
        if (token == null) return false;
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTVerifier jwtVerifier = JWT.require(algorithm).build();
        try {
            DecodedJWT decodedJWT = jwtVerifier.verify(token);
            String id = decodedJWT.getId();//创建时的生成的UUID
            //加入黑名单
            return deleteToken(id,decodedJWT.getExpiresAt());
        } catch (Exception e) {
            return false;
        }
    }

    //删除token：直接将token的uuid放到redis里的黑名单，time为过期时间
    private boolean deleteToken(String uuid,Date time){
        //判断是否已经失效
        if (idInvalidate(uuid))
            return  false;
        //失效
        Date now = new Date();
        long expire = Math.max(time.getTime() - now.getTime(),0);
        stringRedisTemplate.opsForValue().set(JWT_BLACK_LIST+uuid,"",expire, TimeUnit.MILLISECONDS);
        return true;
    }

    //检查uuid是否在黑名单
    private boolean idInvalidate(String uuid){
        //检查Redis里面的黑名单里有没有uuid
        return Boolean.TRUE.equals(stringRedisTemplate.hasKey(JWT_BLACK_LIST+uuid));
    }

}
